package ln.javatest.day12.demo04;
/*
卖票案例出现了线程安全问题：
卖出了不存在的票和重复的票

解决线程安全问题的三种方案：使用Lock锁
java.util.concurrent.locks.Lock接口
Lock实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作
Lock接口中的方法：
    void lock():获取锁
    void unlock()：释放锁
java.util.concurrent.locks.ReentrantLock implements Lock接口
使用步骤：
    1.在成员位置创建一个ReentrantLock对象
    2.在可能会出现安全问题的代码前调用Lock接口中的方法locks获取锁
    3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
*/

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private static int ticket = 100;
    //1.在成员位置创建一个ReentrantLock对象
    Lock l = new ReentrantLock();

    //设置线程任务
    /*@Override
    public void run() {
        //先判断票是否存在
        //用死循环让卖票操作重复执行
        while (true) {
            //2.在可能会出现安全问题的代码前调用Lock接口中的方法locks获取锁
            l.lock();
            if (ticket > 0) {
                //提高安全问题出现的概率，让程序睡眠
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //票存在，卖票
                System.out.println(Thread.currentThread().getName() + "--->正在卖第" + ticket + "张票");
                ticket--;
            }
            //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
            l.unlock();
        }
    }*/
    //下面这个和上面那个实现效果一样，不同在于，下面这个无论程序是否出现异常，锁对象都会被释放掉，提高程序效率
    @Override
    public void run() {
        //先判断票是否存在
        //用死循环让卖票操作重复执行
        while (true) {
            //2.在可能会出现安全问题的代码前调用Lock接口中的方法locks获取锁
            l.lock();
            if (ticket > 0) {
                //提高安全问题出现的概率，让程序睡眠
                try {
                    Thread.sleep(10);
                    //票存在，卖票
                    System.out.println(Thread.currentThread().getName() + "--->正在卖第" + ticket + "张票");
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
                    l.unlock();
                }
            }
        }
    }
}
